package geradores;

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import util.ArquivoUtil;
import analisadorSemantico.LadoDireito;
import analisadorSemantico.Simbolo;
import constante.First;
import constante.Linha;
import constante.Token;

public class GeradorCodigoObjetoAnalisadorSintatico {

	private HashMap<String, Set<LadoDireito>> regrasGramaticais = new HashMap<String, Set<LadoDireito>>();

	private static String nomeArquivoSaida = "AnalisadorSintatico";

	private static String caminhoArquivo = "c:\\gerador\\saida\\";

	private PrintWriter arquivoSaidaAnalisaSintatico;

	private String tab = "";

	private Simbolo simboloResultadoFuncaoFirst = new Simbolo();

	private Boolean temCadeiaVazia = false;

	/**
	 * Construtor
	 * 
	 * @param regras
	 *            Todas as regras
	 * @param arquivoRegras
	 *            arquivo de configuração
	 */
	public GeradorCodigoObjetoAnalisadorSintatico(HashMap<String, Set<LadoDireito>> regras) {

		this.regrasGramaticais = regras;

		try {
			arquivoSaidaAnalisaSintatico = ArquivoUtil.criarArquivo(caminhoArquivo, nomeArquivoSaida + ".java");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Método que Inicia a Geração do Análisador Sintático
	 */
	public void gerar() {

		gerarMetodoBasico();

		Set<String> chaves = this.regrasGramaticais.keySet();
		for (String chave : chaves) {
			if (chave != null)
				gerar(chave, this.regrasGramaticais.get(chave));

		}

		ArquivoUtil.escreverArquivo("}\n", arquivoSaidaAnalisaSintatico);
		fechaArquivo();
	}

	/**
	 * Gera Métodos Básico
	 */
	private void gerarMetodoBasico() {

		// Imports
		ArquivoUtil.escreverArquivo("import java.util.ArrayList;\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("import java.util.List;\n\n\n", arquivoSaidaAnalisaSintatico);

		// Class
		ArquivoUtil.escreverArquivo("public class " + nomeArquivoSaida + " {\n\n", arquivoSaidaAnalisaSintatico);

		ArquivoUtil.escreverArquivo("\tprivate List<Token> tokens;\n\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\tprivate List<String> erros = new ArrayList<String>();\n\n",
				arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\tprivate Integer pToken = 0;\n\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\tprivate Token tokenAtual;\n\n", arquivoSaidaAnalisaSintatico);

		// Exibir lista de erros
		ArquivoUtil.escreverArquivo("\tpublic void mostraErros() {\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t\tfor (String erro : erros) {\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t\t\tSystem.out.println(erro);\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t\t}\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t}\n\n", arquivoSaidaAnalisaSintatico);

		// Construtor
		ArquivoUtil.escreverArquivo("\tpublic " + nomeArquivoSaida + " (List<Token> tokens) {\n",
				arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t\tthis.tokens = tokens;\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t}\n\n", arquivoSaidaAnalisaSintatico);

		// nextToken()
		ArquivoUtil.escreverArquivo("\tprivate void nextToken() {\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t\ttokenAtual = tokens.get(pToken);\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t\tpToken++;\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t}\n\n", arquivoSaidaAnalisaSintatico);

		// look Ahead
		ArquivoUtil.escreverArquivo("\t@SuppressWarnings(\"unused\") \n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\tprivate Token getLookAhead() {\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t\treturn tokens.get(pToken);\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t}\n\n", arquivoSaidaAnalisaSintatico);

		// analisar
		ArquivoUtil.escreverArquivo("\tpublic boolean analisar() {\n", arquivoSaidaAnalisaSintatico);

		ArquivoUtil.escreverArquivo("\tnextToken();\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\tfuncoes();\n", arquivoSaidaAnalisaSintatico);

		ArquivoUtil.escreverArquivo("\tif (tokenAtual.getImagem().equals(\"$\") && erros.isEmpty()) {\n",
				arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\treturn true;\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t} else {\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\terros.add(\"Esperado EOF\");\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\treturn false;\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t}\n", arquivoSaidaAnalisaSintatico);
		ArquivoUtil.escreverArquivo("\t}\n\n", arquivoSaidaAnalisaSintatico);

	}

	/**
	 * Método Para Filtrar os Tipos de Métodos a Ser Gerados
	 * 
	 * @param nomeMetodo
	 * @param ladoDireito
	 */
	private void gerar(String nomeMetodo, Set<LadoDireito> ladoDireito) {

		gerarComentarios(nomeMetodo.toUpperCase(), ladoDireito);
		ArquivoUtil.escreverArquivo("\tpublic void " + nomeMetodo + "(){\n", arquivoSaidaAnalisaSintatico);

		if (ladoDireito.size() == 1) {
			for (LadoDireito ldDireito : ladoDireito) {
				escreverMetodoSimples(ldDireito.getSimbolosLadoDireito());
			}
		} else {
			if (!temSimboloNaoTerminal(ladoDireito)) {
				escreverMetodoComIfNextToken(ladoDireito);
			} else {
				analisarRegraGramatical(ladoDireito);
			}
		}

	}

	/**
	 * Método Para Escrever o Método Simples, sem first
	 * 
	 * @param simbolos
	 */
	private void escreverMetodoSimples(List<Simbolo> simbolos) {

		Integer qtdTabs = 0;
		String stringValores = "";

		for (Simbolo simbolo : simbolos) {

			this.tab = "\t";
			this.tab = gerarTab(qtdTabs);

			if (ehNaoTerminal(simbolo)) {
				stringValores += this.tab + "\t" + gerarStringLinhaAtual(simbolo, Linha.NORMAL) + "\n";
			} else {
				stringValores += this.tab + "if (" + gerarStringLinhaAtual(simbolo, Linha.NORMAL) + " ) {\n";
				stringValores += this.tab + "\tnextToken(); \n";
				qtdTabs++;
			}
		}

		ArquivoUtil.escreverArquivo(stringValores, arquivoSaidaAnalisaSintatico);
		gerarElseMetodoSimples(qtdTabs, simbolos);

	}

	/**
	 * Método Para Escerver Métodos com IF
	 * 
	 * @param ladoDireito
	 */
	private void escreverMetodoComIfNextToken(Set<LadoDireito> ladoDireito) {

		String stringValores = "";
		String temOu = "if (";
		Integer qtdConcatenacoes = 0;
		this.tab = "\t\t";

		for (LadoDireito ldDireito : ladoDireito) {

			for (Simbolo simbolo : ldDireito.getSimbolosLadoDireito()) {

				if (!ehCadeiaVazia(simbolo)) {

					if (qtdConcatenacoes == 1) {
						temOu = " || ";
						this.tab = "";
					}

					stringValores += this.tab + temOu + gerarStringLinhaAtual(simbolo, Linha.NORMAL);

					qtdConcatenacoes++;

				}
			}
		}

		stringValores += "){\n";
		stringValores += "\t\t\tnextToken();\n";
		stringValores += "\t\t}\n";
		stringValores += "\t}\n";
		ArquivoUtil.escreverArquivo(stringValores, arquivoSaidaAnalisaSintatico);

	}

	/**
	 * Método Para Escerver Métodos com IF
	 * 
	 * @param ladoDireito
	 * @param simbolos
	 * @param qtdLoop
	 */
	private void escreverMetodoComIf(Set<LadoDireito> ladoDireito, List<Simbolo> simbolos, Integer qtdLoop) {

		String stringValores = "";

		Integer qtdConcatenacoes = 0;
		this.tab = "\t\t";

		String temOu = "if (";
		if (qtdLoop == 1) {
			temOu = "} else if (";
		}

		for (LadoDireito ldDireito : ladoDireito) {

			for (Simbolo simbolo : ldDireito.getSimbolosLadoDireito()) {

				if (!ehCadeiaVazia(simbolo)) {

					if (qtdConcatenacoes == 1) {
						temOu = " || ";
						this.tab = "";
					}

					stringValores += this.tab + temOu + gerarStringLinhaAtual(simbolo, Linha.NORMAL);
					qtdConcatenacoes++;
				}
			}
		}

		stringValores += "){\n";

		for (Simbolo simbolo : simbolos) {
			stringValores += "\t\t\t " + gerarStringLinhaAtual(simbolo, Linha.NORMAL) + "\n";
		}

		stringValores += "\t\t}\n";
		stringValores += "\t}\n";
		ArquivoUtil.escreverArquivo(stringValores, arquivoSaidaAnalisaSintatico);

	}

	/**
	 * Método Completo/Complexo
	 * 
	 * @param ladoDireito
	 */
	private void analisarRegraGramatical(Set<LadoDireito> ladoDireito) {

		Boolean precisaUtilizarFirst = false;

		this.tab = "\t\t";

		this.temCadeiaVazia = temCadeiaVazia(ladoDireito);

		for (LadoDireito ldDireito : ladoDireito) {

			if (ehNaoTerminal(ldDireito.getSimbolosLadoDireito().get(0))) {
				precisaUtilizarFirst = true;
			}

			/*
			 * Gera Método Com First e Possivel lookAHead
			 */
			if (precisaUtilizarFirst) {
				precisaUtilizarFirst = false;
				escreverMetodoComFirst(ladoDireito);
				break;
			}

			if (!precisaUtilizarFirst && this.temCadeiaVazia
					&& !ehCadeiaVazia(ldDireito.getSimbolosLadoDireito().get(0))) {
				this.temCadeiaVazia = false;
				escreverMetodoSimples(ldDireito.getSimbolosLadoDireito());
				break;

			}
		}

	}

	/**
	 * Gera o método que utilizará first e possivel lookAHead
	 * 
	 * @param ladoDireito
	 */
	private void escreverMetodoComFirst(Set<LadoDireito> ladoDireito) {

		String temElse = " if (";
		Integer qtdLoop = 0;
		Boolean fechar = true;

		Boolean passouLookAHead = false;
		Boolean sair = false;
		Boolean retorno = false;

		for (LadoDireito lDireito : ladoDireito) {

			for (Simbolo item : lDireito.getSimbolosLadoDireito()) {

				if (!ehCadeiaVazia(item)) {

					qtdLoop++;

					if (qtdLoop > 1) {
						temElse = "else if (";
					}

					if (ehNaoTerminal(item)) {

						funcaoFirst(item, First.SIMPLES);
						Simbolo firstItem = this.simboloResultadoFuncaoFirst;

						Set<LadoDireito> ladoDireitoBuscado = buscarRegras(item.getImagem());

						if (ehTerminalLiteral(this.simboloResultadoFuncaoFirst)) {
							escreverMetodoFirstSimples(lDireito.getSimbolosLadoDireito(),
									this.simboloResultadoFuncaoFirst, temElse, false);

							break;

						} else if (ehNaoTerminal(this.simboloResultadoFuncaoFirst)) {

							if (!temSimboloNaoTerminal(ladoDireitoBuscado)) {
								escreverMetodoComIf(ladoDireitoBuscado, lDireito.getSimbolosLadoDireito(), qtdLoop);
								break;

							} else if (ehTudoNaoTerminal(ladoDireitoBuscado)) {
								gerarMetodoComFirst(ladoDireitoBuscado, lDireito.getSimbolosLadoDireito());
								break;

							} else {
								funcaoFirst(item, First.AVANCADO);
								retorno = precisaUtilizarLookAHead(ladoDireitoBuscado, this.simboloResultadoFuncaoFirst);

								// Não Tem Look a head
								if (!retorno) {
									escreverMetodoFirstSimples(lDireito.getSimbolosLadoDireito(), firstItem, temElse,
											false);
									sair = true;
									break;

									// Tem Look a head
								} else {

									if (!passouLookAHead) {
										passouLookAHead = true;
									}

									Simbolo segundoSimboloFirst = pegarTokenLookAHead(item);

									if (segundoSimboloFirst == null) {
										Integer qtdConcatenacoes = 0;
										String stringValores = "";
										String temOu = "if (";

										for (LadoDireito ldDireito : ladoDireitoBuscado) {

											for (Simbolo simbolo : ldDireito.getSimbolosLadoDireito()) {

												if (!ehCadeiaVazia(simbolo)) {

													if (qtdConcatenacoes == 1) {
														temOu = " || ";
														this.tab = "";
													}
													if (ehNaoTerminal(simbolo)) {
														funcaoFirst(item, First.AVANCADO);
														stringValores += this.tab
																+ temOu
																+ gerarStringLinhaAtual(
																		this.simboloResultadoFuncaoFirst, Linha.NORMAL);
													} else {
														stringValores += this.tab + temOu
																+ gerarStringLinhaAtual(simbolo, Linha.NORMAL);
													}

													qtdConcatenacoes++;

												}
											}
										}
										ArquivoUtil.escreverArquivo(stringValores + ") { \n",
												arquivoSaidaAnalisaSintatico);
									} else {
										if (ehNaoTerminal(firstItem)) {
											funcaoFirst(firstItem, First.AVANCADO);
											firstItem = this.simboloResultadoFuncaoFirst;
										}
										ArquivoUtil.escreverArquivo(
												"\t\t" + temElse + gerarStringLinhaAtual(firstItem, Linha.NORMAL)
														+ " && "
														+ gerarStringLinhaAtual(segundoSimboloFirst, Linha.LOOKAHEAD)
														+ ") { \n", arquivoSaidaAnalisaSintatico);
									}

									ArquivoUtil.escreverArquivo("\t\t\t" + gerarStringLinhaAtual(item, Linha.NORMAL)
											+ "\n", arquivoSaidaAnalisaSintatico);
									if (passouLookAHead) {
										ArquivoUtil.escreverArquivo("\t\t}", arquivoSaidaAnalisaSintatico);
									}

								}
							}

						} else {

							funcaoFirst(item, First.AVANCADO);
							retorno = precisaUtilizarLookAHead(ladoDireito, this.simboloResultadoFuncaoFirst);

							// Não Tem Look a head
							if (!retorno) {

								escreverMetodoFirstSimples(lDireito.getSimbolosLadoDireito(), firstItem, temElse, false);
								break;

								// Tem Look a head
							} else {

								if (!passouLookAHead) {
									passouLookAHead = true;
								}

								Simbolo segundoSimboloFirst = pegarTokenLookAHead(item);

								if (segundoSimboloFirst == null) {
									ArquivoUtil.escreverArquivo(
											"\n\t\t " + temElse + gerarStringLinhaAtual(firstItem, Linha.NORMAL)
													+ "){\n", arquivoSaidaAnalisaSintatico);
								} else {
									ArquivoUtil.escreverArquivo(
											"\t\t" + temElse + gerarStringLinhaAtual(firstItem, Linha.NORMAL) + " && "
													+ gerarStringLinhaAtual(segundoSimboloFirst, Linha.LOOKAHEAD)
													+ ") { \n", arquivoSaidaAnalisaSintatico);
								}

								ArquivoUtil.escreverArquivo("\t\t" + gerarStringLinhaAtual(item, Linha.NORMAL) + "\n",
										arquivoSaidaAnalisaSintatico);
								ArquivoUtil.escreverArquivo("\t\t}", arquivoSaidaAnalisaSintatico);
							}
						}
					} else {

						ArquivoUtil.escreverArquivo("\t\t" + temElse + gerarStringLinhaAtual(item, Linha.NORMAL)
								+ "){\n", arquivoSaidaAnalisaSintatico);
						ArquivoUtil.escreverArquivo("\t\t\t nextToken();\n", arquivoSaidaAnalisaSintatico);
						ArquivoUtil.escreverArquivo("\t\t}", arquivoSaidaAnalisaSintatico);
						fechar = true;
					}
				}
			}

			if (sair) {
				break;
			}
		}

		if (fechar) {
			ArquivoUtil.escreverArquivo("\n\t\t}", arquivoSaidaAnalisaSintatico);
		}
	}

	private Set<LadoDireito> buscarRegras(String imagem) {
		return regrasGramaticais.get(imagem);
	}

	/**
	 * Método para pegar o token de decisão do look a head
	 * 
	 * @param item
	 */
	private Simbolo pegarTokenLookAHead(Simbolo item) {

		Integer pos = 1;
		Simbolo segundoSimboloFirst = null;

		Set<LadoDireito> ladoDireitoBuscado = buscarRegras(item.getImagem());

		for (LadoDireito lDireito : ladoDireitoBuscado) {

			Integer qtdLoop = 0;
			for (Simbolo simbolo : lDireito.getSimbolosLadoDireito()) {
				if (qtdLoop == pos) {
					if (ehNaoTerminal(simbolo)) {
						funcaoFirst(simbolo, First.AVANCADO);
						segundoSimboloFirst = this.simboloResultadoFuncaoFirst;
					} else {
						segundoSimboloFirst = simbolo;
					}
				}
				qtdLoop++;
			}
		}

		return segundoSimboloFirst;
	}

	/**
	 * Verifica se precisa utilizar lookAhead
	 * 
	 * @param ladoDireito
	 * 
	 * @return look a head
	 */
	private Boolean precisaUtilizarLookAHead(Set<LadoDireito> ladoDireito, Simbolo simbolo) {

		Simbolo simboloFirstItem = new Simbolo();
		Integer qtdLook = 0;
		for (LadoDireito ldDireito1 : ladoDireito) {
			for (Simbolo item : ldDireito1.getSimbolosLadoDireito()) {

				if (!ehCadeiaVazia(item)) {
					this.simboloResultadoFuncaoFirst = new Simbolo();
					if (ehNaoTerminal(item)) {
						funcaoFirst(item, First.AVANCADO);
						simboloFirstItem = this.simboloResultadoFuncaoFirst;
					}

					if (simboloFirstItem.equals(simbolo)) {
						qtdLook++;
					}

				}
			}
		}

		if (qtdLook <= 1) {
			return false;
		} else {
			return true;
		}

	}

	/**
	 * Valida se o simbolo é Cadeia Vazia
	 * 
	 * @param simbolo
	 * @return true or false
	 */
	private Boolean ehCadeiaVazia(Simbolo simbolo) {

		if (simbolo.getTipo().equalsIgnoreCase(Token.CADEIA_VAZIA.getDescricao())) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Método Para Gerar a String do Token Atual ou look a head
	 * 
	 * @param simbolo
	 * @return String Formada
	 */
	private String gerarStringLinhaAtual(Simbolo simbolo, Linha tipoLinha) {

		String valor = "tokenAtual";

		if (tipoLinha.equals(Linha.LOOKAHEAD)) {
			valor = "getLookAhead()";
		}

		if (ehTerminalClasse(simbolo)) {
			return valor + ".getClasse() == Classe." + simbolo.getImagem().toUpperCase();
		} else if (ehTerminalLiteral(simbolo)) {
			return valor + ".getImagem().equals(\"" + simbolo.getImagem() + "\")";
		} else if (ehNaoTerminal(simbolo)) {
			return simbolo.getImagem() + "();";
		} else {
			return "";
		}
	}

	/**
	 * Gera método que utiliza a função first sem lookaHead
	 * 
	 * @param simbolos
	 * @param simboloFirst
	 * @param ifElse
	 * @param fechar
	 */
	private void escreverMetodoFirstSimples(List<Simbolo> simbolos, Simbolo simboloFirst, String ifElse, Boolean fechar) {

		String stringValores = "";
		this.tab = "\t\t";

		stringValores += "\t\t" + ifElse + (gerarStringLinhaAtual(simboloFirst, Linha.NORMAL)) + ") {\n";
		for (Simbolo item : simbolos) {
			stringValores += "\t\t\t" + gerarStringLinhaAtual(item, Linha.NORMAL) + "\n";
		}
		stringValores += "\t\t}\n";

		if (fechar) {
			stringValores += "\t}\n";
		}

		ArquivoUtil.escreverArquivo(stringValores, arquivoSaidaAnalisaSintatico);

	}

	/**
	 * Gera o método que utiliza first
	 * 
	 * @param ladoDireito
	 * @param simbolos
	 */
	private void gerarMetodoComFirst(Set<LadoDireito> ladoDireito, List<Simbolo> simbolos) {

		String stringGerada = "\t\t";
		String temOu = "if (";
		Integer qtd = 0;

		/*
		 * Para cada símbolo lido, fazer o first e concatenar para montar o if
		 */
		for (LadoDireito lDireito : ladoDireito) {
			for (Simbolo item : lDireito.getSimbolosLadoDireito()) {

				if (qtd > 0) {
					temOu = " || ";
				}

				if (!ehCadeiaVazia(item)) {

					stringGerada += temOu;
					if (ehNaoTerminal(item)) {
						funcaoFirst(item, First.AVANCADO);
						stringGerada += gerarStringLinhaAtual(this.simboloResultadoFuncaoFirst, Linha.NORMAL);
					} else {
						stringGerada += gerarStringLinhaAtual(item, Linha.NORMAL);
					}

					qtd++;
				}
			}
		}

		stringGerada += ") { \n";
		for (Simbolo simbolo : simbolos) {
			stringGerada += "\t\t\t" + gerarStringLinhaAtual(simbolo, Linha.NORMAL) + "\n";
		}

		stringGerada += "\t\t} \n";
		ArquivoUtil.escreverArquivo(stringGerada, arquivoSaidaAnalisaSintatico);

	}

	/**
	 * Esse método será responsável por decidir qual será o símbolo lookaHead
	 * 
	 * @param simbolo
	 * @param tipoFirst
	 */
	private void funcaoFirst(Simbolo simbolo, First tipoFirst) {

		if (regrasGramaticais.containsKey(simbolo.getImagem())) {

			Set<LadoDireito> ladoDireitoBuscado = buscarRegras(simbolo.getImagem());

			for (LadoDireito ladoDireito : ladoDireitoBuscado) {
				if (!ehCadeiaVazia(ladoDireito.getSimbolosLadoDireito().get(0))) {
					this.simboloResultadoFuncaoFirst = ladoDireito.getSimbolosLadoDireito().get(0);
					break;
				}
			}

			if (tipoFirst.equals(First.AVANCADO)) {
				if (ehNaoTerminal(this.simboloResultadoFuncaoFirst)) {
					funcaoFirst(this.simboloResultadoFuncaoFirst, First.AVANCADO);
				}
			}

		}
	}

	/**
	 * Valida se o simbolo é Não Terminal
	 * 
	 * @param simbolo
	 * @return true or false
	 */
	private Boolean ehNaoTerminal(Simbolo simbolo) {

		if (simbolo.getTipo().equals(Token.NAO_TERMINAL.getDescricao())) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Valida se o simbolo é terminal literal
	 * 
	 * @param simbolo
	 * @return true or false
	 */
	private Boolean ehTerminalLiteral(Simbolo simbolo) {

		if (simbolo.getTipo().equalsIgnoreCase(Token.TERMINAL_LITERAL.getDescricao())) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Valida se o simbolo é terminal classe
	 * 
	 * @param simbolo
	 * @return true or false
	 */
	private Boolean ehTerminalClasse(Simbolo simbolo) {

		if (simbolo.getTipo().equalsIgnoreCase(Token.TERMINAL_CLASSE.getDescricao())) {
			return true;
		} else {
			return false;
		}

	}

	/**
	 * Verifica se possui cadeia Vazia
	 * 
	 * @param ladoDireito
	 * @return
	 */
	private Boolean temCadeiaVazia(Set<LadoDireito> ladoDireito) {

		for (LadoDireito ldDireito : ladoDireito) {
			for (Simbolo item : ldDireito.getSimbolosLadoDireito()) {
				if (ehCadeiaVazia(item)) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Verifica se no ladoDireito possui algum simbolo do tipo não terminal
	 * 
	 * @param ladoDireito
	 * @return
	 */
	private boolean temSimboloNaoTerminal(Set<LadoDireito> ladoDireito) {

		for (LadoDireito ldDireito : ladoDireito) {
			for (Simbolo item : ldDireito.getSimbolosLadoDireito()) {
				if (ehNaoTerminal(item)) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * 
	 * Verifica se todos os símbolos são não terminais
	 * 
	 * @param ladoDireitoBuscado
	 * @return
	 */
	private boolean ehTudoNaoTerminal(Set<LadoDireito> ladoDireitoBuscado) {

		for (LadoDireito ladoDireito : ladoDireitoBuscado) {
			for (Simbolo simbolo : ladoDireito.getSimbolosLadoDireito()) {
				if (!ehNaoTerminal(simbolo)) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Cria os tabs \ts
	 * 
	 * @param pos
	 * @return
	 */
	private String gerarTab(Integer pos) {

		if (pos == 0) {
			this.tab += "\t";
		} else {
			this.tab += "\t";
			gerarTab(pos - 1);
		}

		return tab;
	}

	/**
	 * Cria a quantidade de } para os Else
	 * 
	 * @param pos
	 * @param simbolos
	 */
	private void gerarElseMetodoSimples(Integer pos, List<Simbolo> simbolos) {

		ArrayList<Simbolo> simbolosInvertido = new ArrayList<>();
		// Fecha a quantidade de Chaves
		Integer i = simbolos.size() - 1;
		while (i != -1) {
			simbolosInvertido.add(simbolos.get(i));
			i--;
		}

		for (Simbolo item : simbolosInvertido) {
			if (!item.getTipo().equals("NAO_TERMINAL") && this.temCadeiaVazia) {
				ArquivoUtil.escreverArquivo("} else erros.add(\"Esperado Símbolo: '" + item.getImagem() + "'"
						+ "\");  \n", arquivoSaidaAnalisaSintatico);
			}
		}

		if (simbolos.size() == 1) {
			ArquivoUtil.escreverArquivo("} \n", arquivoSaidaAnalisaSintatico);
		} else if (!this.temCadeiaVazia) {
			for (int j = 0; j < pos + 1; j++) {
				ArquivoUtil.escreverArquivo("} \n", arquivoSaidaAnalisaSintatico);
			}
		} else {
			ArquivoUtil.escreverArquivo("} \n", arquivoSaidaAnalisaSintatico);
		}

	}

	/**
	 * Método para gerar comentários, esses comentários vão em cima da linha de
	 * cada método
	 * 
	 * @param nomeMetodo
	 * @param ladoDireito
	 */
	private void gerarComentarios(String nomeMetodo, Set<LadoDireito> ladoDireito) {

		String valorLinha = "\n\n";
		Integer qtd = 0;

		valorLinha += "\t//<" + nomeMetodo.toLowerCase() + "> ::= ";
		for (LadoDireito lDireito : ladoDireito) {

			if (qtd > 0) {
				valorLinha += " | ";
			}
			qtd++;

			for (Simbolo simbolo : lDireito.getSimbolosLadoDireito()) {
				if (ehTerminalLiteral(simbolo)) {
					valorLinha += " '" + simbolo.getImagem().toLowerCase() + "'";
				} else if (ehNaoTerminal(simbolo)) {
					valorLinha += " <" + simbolo.getImagem().toLowerCase() + ">";
				} else if (ehTerminalClasse(simbolo)) {
					valorLinha += " " + simbolo.getImagem().toLowerCase();
				} else if (ehCadeiaVazia(simbolo) && !valorLinha.endsWith(" | ")) {
					valorLinha += " | ";
				}

			}
		}
		valorLinha += " ; \n";
		ArquivoUtil.escreverArquivo(valorLinha, arquivoSaidaAnalisaSintatico);
	}

	/**
	 * Método para fechar o arquivo
	 */
	private void fechaArquivo() {
		ArquivoUtil.fecharArquivo(this.arquivoSaidaAnalisaSintatico);

	}
}
